#include "ClientTCP.h"
#include "Logger.h"

#define MAXPENDING 5

ClientTCP::ClientTCP()
{
	connected = false;
}

ClientTCP::ClientTCP(int _sockfd)
{
	connected = false;
	sockfd = _sockfd;
}

void ClientTCP::connect(QString s)
{
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		//error handling
		throw NetworkException("Error on connect: getting socket");
	}

	server = gethostbyname(s.toLocal8Bit().data());
	//server = gethostbyname("127.0.0.1");
	if (server == NULL)
	{
		//error handling
		throw NetworkException("Error on connect: getting hostname");
	}

	bzero((char *) &serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	bcopy((char *) server->h_addr, (char *) &serv_addr.sin_addr.s_addr,
			server->h_length);

	serv_addr.sin_port = htons(PORT_NUMBER);
	if (::connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
			< 0)
	{
		//error handling
		throw NetworkException("Error on connect: connecting");
	}
	connected = true;
}

void ClientTCP::sendToServer(int d, int n = 0, void *args = NULL)
{
	Logger::log("Sending command: %d with %d arguments", d, n);
	if (send(sockfd, (void*) &d, sizeof(int), 0) < 0)
	{
		throw NetworkException("Error on sending data to server");
	}
	if (send(sockfd, (void*) &n, sizeof(int), 0) < 0)
	{
		throw NetworkException("Error on sending data to server");
	}
	if (n > 0)
	{
		if (send(sockfd, (void*) args, n, 0) < 0)
		{
			throw NetworkException("Error on sending data to server");
		}
	}
}

void ClientTCP::initServer()
{
	if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
	{
		//error handling
		throw NetworkException("Error on initializing server: socket");
	}
	int on = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(PORT_NUMBER);
	if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
	{
		//error handling
		throw NetworkException("Error on doing the bind");
	}
	else
	{
		Logger::log("Listening on port: %d", PORT_NUMBER);
	}
	if (listen(sockfd, MAXPENDING) < 0)
	{
		throw NetworkException("Error on listening server socket");
	}
}

ClientTCP* ClientTCP::acceptConnection()
{
	int clientsock;
	struct sockaddr_in client;

	unsigned int clientlen = sizeof(client);
	if ((clientsock = accept(sockfd, (struct sockaddr *) &client, &clientlen))
			< 0)
	{
		sleep(1);
		//error handling
		throw NetworkException("Error on getting client socket");
	}

	Logger::log("Client connected: %s", inet_ntoa(client.sin_addr));

	return new ClientTCP(clientsock);
}

void ClientTCP::sendToClient(int d)
{
	//	if (send(sockfd, (void*) &d, sizeof(d), 0) < 0)
	//	{
	//		//error handling
	//		throw NetworkException("Error sending data to client");
	//	}
}

int ClientTCP::receive()
{
	int d;
	if (read(sockfd, &d, sizeof(int)) <= 0)
	{
		throw NetworkException(QString("Failed to read from socket!"));
	}
	int n;
	if (read(sockfd, &n, sizeof(int)) <= 0)
	{
		throw NetworkException(QString("Failed to read from socket!"));
	}
	parameterCount = n;
	if (n > 0)
	{
		char* data = new char[n];
		if (read(sockfd, (void*) &data, n) <= 0)
		{
			throw NetworkException(QString("Failed to read from socket!"));
		}
		parameters = data;
	}

	return d;
}

int ClientTCP::getParameterCount()
{
	return parameterCount;
}

void* ClientTCP::getParameters()
{
	return parameters;
}

char* ClientTCP::receiveConnect()
{
	int size;
	if (read(sockfd, &size, sizeof(int)) <= 0)
	{
		throw NetworkException(QString("Failed to read from socket!"));
	}
	char *name = new char[size + 1];
	if (read(sockfd, name, sizeof(char) * (size + 1)) <= 0)
	{
		throw NetworkException(QString("Failed to read from socket!"));
	}
	return name;
}

void ClientTCP::close()
{
	::close(sockfd);
	sockfd = 0;
	connected = false;
}

bool ClientTCP::isOpen()
{
	return sockfd > 0;
}

bool ClientTCP::isConnected()
{
	return connected;
}

void ClientTCP::sendRobotName(const char *name)
{
	int len = strlen(name) + 1;
	if (send(sockfd, (void*) &len, sizeof(int), 0) < 0)
	{
		throw NetworkException("Error on sending data to server");
	}
	if (send(sockfd, (void*) name, sizeof(char) * len, 0) < 0)
	{
		throw NetworkException("Error on sending data to server");
	}
}
